Data Processing

Read the CoGo (a bike-sharing program) trip data for the year 2019, which is aggregated by Census Block Groups (CBGs).

cogo_2019<-read.csv("Data/cogodata_2019.csv")
cogo_volume<-cogo_2019%>% group_by(From_ID,To_ID)%>%summarise(f_n=sum(count))
## `summarise()` has grouped output by 'From_ID'. You can override using the
## `.groups` argument.
names(cogo_volume)<-c("oid", "did","n")
cogo_volume$oid <- as.character(cogo_volume$oid)
cogo_volume$did <- as.character(cogo_volume$did)

We import a shapefile containing the boundaries of Ohio’s CBGs and filter to only retain the CBGs involved in the bike-sharing trips.

shape <-st_read('Data/Franklin_BG_1.shp')
## Reading layer `Franklin_BG_1' from data source 
##   `/Users/hailyeeha/Dropbox/hailee/PhD_works/Pre_Study/SNA_Paper/spnaf/Data/Franklin_BG_1.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 9472 features and 1 field
## Geometry type: POLYGON
## Dimension:     XY
## Bounding box:  xmin: -84.8203 ymin: 38.40342 xmax: -80.5187 ymax: 42.32713
## Geodetic CRS:  NAD83
shape <- shape %>% filter(id %in% cogo_volume$oid | id %in% cogo_volume$did)

Gij* Calculating

We calculate Gij* using the ‘Gij.polygon’ function. And we select 0.05 of p-value as a significance level.

result <- Gij.polygon(df = cogo_volume ,shape = shape , queen = TRUE, snap = 1, method = 't')
## (1) Creating Spatial Weights...
## The legacy packages maptools, rgdal, and rgeos, underpinning this package
## will retire shortly. Please refer to R-spatial evolution reports on
## https://r-spatial.org/r/2023/05/15/evolution4.html for details.
## This package is now running under evolution status 0
## Done !
## note: Total 2756 network combinations are ready to be analyzed
## note: which is calculated by the input polygon data
## (2) Calculating Gij ... Done! 
## (3) Do bootstrapping ... Done ! 
## (4) Generating result in lines ...
## Warning: st_centroid assumes attributes are constant over geometries
## Done !
result<-as.data.frame(result[[2]])
cogo_pval<-subset(result, result$pval<0.05)
head(cogo_pval)
##     oid  did   n       Gij  pval                            sfc
## 1  1275 1319  44  5.400743 0.014 LINESTRING (-83.00605 39.97...
## 2  1275 1320  50  4.323812 0.019 LINESTRING (-83.00605 39.97...
## 4  1275 2126  72  1.928539 0.047 LINESTRING (-83.00605 39.97...
## 11 1275 2250  15  2.803685 0.044 LINESTRING (-83.00605 39.97...
## 15 1275 2279  35  1.669849 0.048 LINESTRING (-83.00605 39.97...
## 17 1275 2814 208 10.126094 0.009 LINESTRING (-83.00605 39.97...

Visualization

We now create interactive visualizations of the results using the {mapdeck} package. Remember to replace pk.abc with your own mapbox token to make sure the interactive map works.

key<-'pk.abc' ## put your own token here
set_token(key)

We open the file including geometry information of each CBG.

id_geometry<-read.csv("Data/id_geometry_f.csv")

We first visualize the total flow(>20) on the interactive map. We merge the previously created ‘cogo_volume’ dataset with the ‘id_geometry’ dataset by ‘oid’ and ‘id’, and then again by ‘did’ and ’id.This process links trip volume data with corresponding geometric information.

total_geo<-merge(cogo_volume,id_geometry,by.x="oid",by="id")
total_geo<-merge(total_geo,id_geometry,by.x="did",by="id")

We then filter out rows where ‘oid’ is equal to ‘did’ to remove trips that start and end in the same place.

total_geo<-subset(total_geo,total_geo$oid!=total_geo$did)
total_map<-as.data.frame(cbind(total_geo$x.x,total_geo$y.x,total_geo$x.y,total_geo$y.y,total_geo$n))

We rename the data.frame columns to denote the start and end coordinates of each trip.

names(total_map)<-c("start_lon", "start_lat","end_lon", "end_lat","n")

We generate an ‘id’ column to assign unique identifiers to each row and a ‘stroke’ column to determine the width of the lines to be drawn on the map.

total_map$id<-seq_len(nrow(total_map))
total_map$stroke <- round((percent_rank(total_map$n)*5),digits = 3)

We filter the data to only include trips that have a total volume of more than 20.

total_map<-subset(total_map,total_map$n>20)

We use the quintiles to categorize the trip volumes.Thus, we first define the range for the ‘n’(trip volume) variable. Then we create the break points for the quintile scales of the ‘n’. After these processes, we create ‘n_cat’ variable to cut ‘n’ into quintiles

n_range <- range(total_map$n)
break_points <- quantile(total_map$n, probs = seq(0, 1, 0.2), na.rm = TRUE)
total_map$n_cat <- cut(total_map$n, breaks = break_points, labels = FALSE, include.lowest = TRUE)

And we assign the colors to each quintile category using the color palette.

color_func <- colorRampPalette(c("#C6DBEF", "#9ECAE1", "#4292C6","#2171B5", "#084594"))
palette <- color_func(5)
total_map$n_color <- palette[as.integer(total_map$n_cat)]

We create an interactive map with arcs representing the bike trips.The color of each arc are determined by the number of trips (volume).

mapdeck(token = key, style = mapdeck_style("streets"), pitch = 45) %>%
  add_arc(
    data = total_map,
    layer_id = "arcs",
    origin = c("start_lon", "start_lat"),
    destination = c("end_lon", "end_lat"),
    stroke_from = "n_color",
    stroke_to = "n_color",
    stroke_from_opacity = 0.6,
    stroke_to_opacity = 0.6,
    stroke_width = "stroke",
    auto_highlight = TRUE
  )

CoGo bike sharing trip flows (>20 trip volumes) (2019)

Next, we visualize flow hot-spots with a p-value of 0.05. Follow similar steps as in the total flow visualization, but this time merging ‘cogo_pval’ with ‘id_geometry’.

gij_geo<-merge(cogo_pval,id_geometry,by.x="oid",by="id")
gij_geo<-merge(gij_geo,id_geometry,by.x="did",by="id")
gij_map<-as.data.frame(cbind(gij_geo$x.x,gij_geo$y.x,gij_geo$x.y,gij_geo$y.y,gij_geo$Gij,gij_geo$pval))
names(gij_map)<-c("start_lon", "start_lat","end_lon", "end_lat","Gij","pval")
gij_map$id<-seq_len(nrow(gij_map))
gij_map$stroke <- round((percent_rank(gij_map$Gij)*5),digits = 3)

In this case, we use the quintiles for categorize Gij* values. First we define the range for the ‘Gij’ variable(Gij*).

n_range <- range(gij_map$Gij)
break_points <- quantile(gij_map$Gij, probs = seq(0, 1, 0.2), na.rm = TRUE)
gij_map$n_cat <- cut(gij_map$Gij, breaks = break_points, labels = FALSE, include.lowest = TRUE)

We define color palette for the hot-spots. We use the red colors to visualize the hot-spots. We assign the red colors to the quintile scales of the Gij* values.

color_func <- colorRampPalette(c("#FFF5F0", "#FCBBA1", "#FC9272", "#CB181D", "#99000D")) 
palette <- color_func(5) 
gij_map$n_color <- palette[as.integer(gij_map$n_cat)]
gij_map$n_color <- palette[as.integer(gij_map$n_cat)]

Finally, we create an interactive map with arcs representing the Gij* vlaues of bike sharing hot-spots.

The color of each arc are determined by the Gij* values.

mapdeck(token = key, style = mapdeck_style("streets"), pitch = 45) %>%
  add_arc(
    data = gij_map,
    layer_id = "arcs",
    origin = c("start_lon", "start_lat"),
    destination = c("end_lon", "end_lat"),
    stroke_from = "n_color",
    stroke_to = "n_color",
    stroke_from_opacity = 0.6,
    stroke_to_opacity = 0.6,
    stroke_width = gij_map$Gij* 3, # assuming 'n' also determines width of arcs
    auto_highlight = TRUE
  )

CoGo bike-sharing flow hotspots (2019)